#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////

int NoeudSeqInst::getValeur() {
    int valeur = 0;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        valeur = tabInst[i]->getValeur(); // on evalue chaque instruction de la séquence
    return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        tabInst[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}

void NoeudSeqInst::AfficheCompileCpp(ofstream& fichier, unsigned short indentation) {
    Noeud::AfficheCompileCpp(fichier, indentation);

    for (unsigned int i = 0; i < tabInst.size(); i++) {
        tabInst[i]->AfficheCompileCpp(fichier, indentation + 1); // on evalue chaque instruction de la séquence
        cout << endl;
        fichier << endl;
    }



    // par convention, resultat = valeur de la derniere instruction}
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable = variable;
    this->expression = expression;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudAffectation::getValeur() {

    int valeur = expression->getValeur(); // on évalue l'expression
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Affectation" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
    expression->afficher(indentation + 1); // en augmentant l'indentation
}

void NoeudAffectation::AfficheCompileCpp(ofstream& fichier, unsigned short indentation) {
    variable->AfficheCompileCpp(fichier, indentation);
    cout << " = ";
    fichier << " = ";
    expression->AfficheCompileCpp(fichier);
    cout << ";";
    fichier << ";";
}

void NoeudAffectation::AfficheCompileCppSp(ofstream& fichier, unsigned short indentation) {
    variable->AfficheCompileCpp(fichier, indentation);
    cout << " = ";
    fichier << " = ";
    expression->AfficheCompileCpp(fichier);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurBinaire::getValeur() {
    int valeur = 0;
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();
    if (this->operateur == "+") valeur = og + od;
    else if (this->operateur == "-") valeur = og - od;
    else if (this->operateur == "*") valeur = og * od;
    else if (this->operateur == "et") valeur = og && od;
    else if (this->operateur == "ou") valeur = og || od;
    else if (this->operateur == "<") valeur = og < od;
    else if (this->operateur == "<=") valeur = og <= od;
    else if (this->operateur == "==") valeur = og == od;
    else if (this->operateur == "!=") valeur = og != od;
    else if (this->operateur == ">") valeur = og > od;
    else if (this->operateur == ">=") valeur = og >= od;
    else { // this->operateur=="/"
        try {
            if (od != 0) {
                valeur = og / od;
            } else {
                valeur = og;
                throw 1;
            }
        } catch (int) {
            cout << "Erreur pendant l'interpretation : division par zero" << endl;
        }
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

void NoeudOperateurBinaire::AfficheCompileCpp(ofstream& fichier, unsigned short indentation) {
    Noeud::AfficheCompileCpp(fichier, indentation);
    operandeGauche->AfficheCompileCpp(fichier, indentation);
    cout << " ";
    fichier << " ";

    string str = this->operateur.getChaine();
    if (str == "et") {
        str = "&&";
    } else if (str == "ou") {
        str = "||";
    }
    cout << str;
    fichier << str;

    operandeDroit->AfficheCompileCpp(fichier, 0);
}
////////////////////////////////////////////////////////////////////////////////

NoeudBoucle::NoeudBoucle(Noeud* condition, Noeud* seqInst) {
    this->condition = condition;
    this->seqInst = seqInst;
}

int NoeudBoucle::getValeur() {
    return 0;
} // évalue chaque instruction de la séquence

void NoeudBoucle::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    condition->afficher(indentation + 1);
    seqInst->afficher(indentation + 1);
}

void NoeudBoucle::AfficheCompileCpp(ofstream& fichier, unsigned short indentation) {
    Noeud::AfficheCompileCpp(fichier, indentation + 1);
}
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(Noeud* condition, Noeud* seqInst) :
NoeudBoucle::NoeudBoucle(condition, seqInst) {
}

int NoeudInstTq::getValeur() {
    int valeur = 0;
    while (condition->getValeur()) {
        valeur = seqInst->getValeur();
    }
    return valeur;
}

void NoeudInstTq::afficher(unsigned short indentation) {
    cout << "Noeud - InstTantque" << endl;
    NoeudBoucle::afficher(indentation);
}

void NoeudInstTq::AfficheCompileCpp(ofstream& fichier, unsigned short indentation) {
    NoeudBoucle::AfficheCompileCpp(fichier, indentation);
    cout << "While( ";
    fichier << "While( ";

    condition->AfficheCompileCpp(fichier);
    cout << " ){" << endl;
    fichier << " ){" << endl;

    seqInst->AfficheCompileCpp(fichier, indentation + 1);
    cout << "}" << endl;
    fichier << "}" << endl;

}
////////////////////////////////////////////////////////////////////////////////

NoeudRepeter::NoeudRepeter(Noeud* condition, Noeud* seqInst) :
NoeudBoucle::NoeudBoucle(condition, seqInst) {
}

int NoeudRepeter::getValeur() {
    int valeur = 0;
    do {
        valeur = seqInst->getValeur();
    } while (condition->getValeur());
    return valeur;
}

void NoeudRepeter::afficher(unsigned short indentation) {
    cout << "Noeud - Repeter" << endl;
    NoeudBoucle::afficher(indentation);
}

void NoeudRepeter::AfficheCompileCpp(ofstream& fichier, unsigned short indentation) {
    NoeudBoucle::AfficheCompileCpp(fichier, indentation);
    cout << "do{"<<endl;
    fichier << "do{"<<endl;

    seqInst->AfficheCompileCpp(fichier, indentation + 1);
    
    NoeudBoucle::AfficheCompileCpp(fichier, indentation);
    cout << "}" << endl;
    NoeudBoucle::AfficheCompileCpp(fichier, indentation);
    cout <<"while(";
    fichier << "}" << endl << "while(";

    condition->AfficheCompileCpp(fichier);
    cout << " );" << endl;
    fichier << " );" << endl;

}
////////////////////////////////////////////////////////////////////////////////

NoeudPour::NoeudPour(Noeud* condition, Noeud* seqInst, Noeud* affInit, Noeud* affIter)
: NoeudBoucle::NoeudBoucle(condition, seqInst) {
    this->affInit = affInit;
    this->affIter = affIter;
}

int NoeudPour::getValeur() {
    for (int i = affInit->getValeur(); condition->getValeur(); i = i + affIter->getValeur()) {
        seqInst->getValeur();
    }
    return 0;
}

void NoeudPour::afficher(unsigned short indentation) {
    NoeudBoucle::afficher(indentation);
    cout << "detail: Noeud - For" << endl;
    affInit->afficher(indentation + 1);
    affIter->afficher(indentation + 1);
}

void NoeudPour::AfficheCompileCpp(ofstream& fichier, unsigned short indentation) {
    NoeudBoucle::AfficheCompileCpp(fichier, indentation);
    cout << "for(";
    fichier << "for(";
    affInit->AfficheCompileCpp(fichier,0);
    condition->AfficheCompileCpp(fichier,0);
    cout << ";";
    fichier << ";";
    ((NoeudAffectation*) affIter)->AfficheCompileCppSp(fichier,0);
    cout << " ){" << endl;
    fichier << " ){" << endl;

    seqInst->AfficheCompileCpp(fichier, indentation + 1);
    cout << "}" << endl;
    fichier << "}" << endl;

}
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi(Noeud* condition, Noeud* seqInst, Noeud* sinonsi, Noeud* seqSinon, bool estSinonsi) {
    this->condition = condition;
    this->seqInst = seqInst;
    this->sinonsi = sinonsi;
    this->seqSinon = seqSinon;
    this->estSinonsi = estSinonsi;

}

void NoeudInstSi::ajouteQueueSinonsi(Noeud* condition, Noeud* seqInst) {
    Noeud* pNoeud = this;
    while (((NoeudInstSi*) pNoeud)->sinonsi != NULL) {
        pNoeud = ((NoeudInstSi*) pNoeud)->sinonsi;
    }
    ((NoeudInstSi*) pNoeud)->sinonsi = new NoeudInstSi(condition, seqInst, NULL, NULL,true);
}

int NoeudInstSi::getValeur() {
    int valeur = 1;
    if (this != NULL) {
        valeur = 0;
        if (condition->getValeur()) {
            valeur = seqInst->getValeur();
        } else {
            if (sinonsi != NULL) {
                valeur = sinonsi->getValeur();
            } else {
                valeur = 1;
            }
            if (valeur == 1 && seqSinon != NULL) {
                valeur = seqSinon->getValeur();
            }
        }
    }
    return valeur;
}

void NoeudInstSi::afficher(unsigned short indentation) {
    if (this != NULL) {
        Noeud::afficher(indentation);
        cout << "Noeud - InstSi" << endl;
        condition->afficher(indentation + 1);
        seqInst->afficher(indentation + 1);
        if (sinonsi != NULL) {
            cout << "Sinonsi---------------" << endl;
            sinonsi->afficher(indentation + 1);
        }
        if (seqSinon != NULL) {
            cout << "Sinon---------------:" << endl;
            seqSinon->afficher(indentation + 1);
        }
    }
}

void NoeudInstSi::AfficheCompileCpp(ofstream& fichier, unsigned short indentation) {
    if (this != NULL) {
       Noeud::AfficheCompileCpp(fichier, indentation);
        if (estSinonsi){
            cout << "else if(";
            fichier << "else if(";
        }
        else{
            cout << "if(";
            fichier << "if(";
        }
        condition->AfficheCompileCpp(fichier);

        
        cout << "){" << endl;
        fichier << "){" << endl;
        seqInst->AfficheCompileCpp(fichier, indentation + 1);
        cout << "}" << endl;
        fichier << "}" << endl;
        if (sinonsi != NULL) {
            sinonsi->AfficheCompileCpp(fichier,indentation);
        }
        if (seqSinon != NULL) {
            Noeud::AfficheCompileCpp(fichier, indentation);
            cout << "else{"<<endl;
            fichier << "else{"<<endl;            
            seqSinon->AfficheCompileCpp(fichier, indentation + 1);
            cout << "}";
            fichier << "}";
        }
    }
}
////////////////////////////////////////////////////////////////////////////////

NoeudInstLire::NoeudInstLire(Noeud* variable) {
    this->variable = variable;
}

int NoeudInstLire::getValeur() {
    int valeur;
    cin >> valeur;
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur;
}

void NoeudInstLire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Lire" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
}

void NoeudInstLire::AfficheCompileCpp(ofstream& fichier, unsigned short indentation) {
    Noeud::AfficheCompileCpp(fichier, indentation);
    cout << "cin >>";
    fichier << "std::cin >>";
    variable->AfficheCompileCpp(fichier, indentation);
    cout << ";";
    fichier << ";";

}
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire::NoeudInstEcrire(Noeud* variable) {
    this->variable = variable;
}

void NoeudInstEcrire::ecrire() { //affiche la valeur desire
    if (*((SymboleValue*) variable) == "<CHAINE>") {
        std::string chaine = ((SymboleValue*) variable)->getChaine();
        chaine.erase(chaine.begin());
        chaine.erase(chaine.end() - 1);
        cout << chaine;
    } else {
        cout << ((SymboleValue*) variable)->getValeur();
    }
}

int NoeudInstEcrire::getValeur() {
    this->ecrire();
    //cout<<((SymboleValue*) variable)->getChaine();
    return ((SymboleValue*) variable)->getValeur();
}

void NoeudInstEcrire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Ecrire" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
} // affiche la séquence d'instructions

void NoeudInstEcrire::AfficheCompileCpp(ofstream& fichier, unsigned short indentation) {
    Noeud::AfficheCompileCpp(fichier, indentation);
    cout << "cout <<";
    fichier << "std::cout <<";
    variable->AfficheCompileCpp(fichier, 0);
    cout << ";";
    fichier << ";";

}
////////////////////////////////////////////////////////////////////////////////